/**
 * Copyright (C) 2009 Matteo Merli <mm@merlimat.org>
 * 
 * $Id: PxlModule.java 14 2009-01-04 18:01:41Z matteo.merli $
 * $URL: http://pxl-lang.googlecode.com/svn/trunk/src/main/java/pxl/types/PxlModule.java $
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pxl.types;

import java.io.File;
import java.util.Collection;

import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;

import pxl.Context;
import pxl.GlobalScope;
import pxl.ModuleCache;
import pxl.PxlTokenSource;
import pxl.Scope;
import pxl.Main.MyLexer;
import pxl.errors.ImportError;
import pxl.errors.SyntaxError;
import pxl.grammar.classes.PxlLexer;
import pxl.grammar.classes.PxlParser;
import pxl.grammar.classes.PxlTreeWalker;

public class PxlModule extends PxlObject
{
    private final String name;
    private final Scope scope;

    public PxlModule( String name, Scope scope )
    {
        this.name = name;
        this.scope = scope;
    }

    public String name()
    {
        return name;
    }

    @Override
    public String getType()
    {
        return "module";
    }

    // Attributes
    @Override
    protected PxlObject getAttribute( String name )
    {
        return scope.get( name );
    }

    @Override
    protected Collection<String> getAttributeNames()
    {
        return scope.getSymbols();
    }

    // /
    public static PxlModule importModule( String name )
    {
        Context ctx = Context.getInstance();
        PxlModule res = ModuleCache.instance().get( name );
        if ( res != null )
        {
            ctx.put( name, res );
            return res;
        }

        // Try to parse from pyhon source
        File currentDir;
        if ( ctx.has( "__file__" ) )
        {
            String currentFile = ((PxlString) (ctx.get( "__file__" )))
                    .toString();
            currentDir = new File( currentFile ).getParentFile();
        }
        else
        {
            currentDir = new File( "." );
        }

        File file = new File( currentDir, name + ".py" );
        if ( file.exists() )
        {
            res = importFromSource( name, file.toString() );
        }
        else
        {
            File dir = new File( currentDir, name );
            file = new File( dir, "__init__.py" );
            if ( file.exists() )
                res = importFromSource( name, file.toString() );
        }

        if ( res == null )
            throw new ImportError( name );

        ModuleCache.instance().put( res );
        return res;
    }

    private static PxlModule importFromSource( String name, String fileName )
    {
        CharStream stream = null;
        CommonTokenStream tokens = null;
        PxlLexer lexer = null;
        PxlParser parser = null;

        Scope moduleScope = new Scope( fileName, GlobalScope.getInstance() );

        try
        {
            stream = new ANTLRFileStream( fileName );
            lexer = new MyLexer( stream );
            tokens = new CommonTokenStream( lexer );
            tokens.discardOffChannelTokens( true );
            PxlTokenSource indentedSource = new PxlTokenSource( tokens );
            tokens = new CommonTokenStream( indentedSource );

            parser = new PxlParser( tokens );

            Context ctx = Context.getInstance();
            ctx.push( moduleScope );
            ctx.put( "__file__", new PxlString( fileName ) );

            // ////////////////
            PxlParser.file_input_return ret = parser.file_input();
            int errors = parser.getNumberOfSyntaxErrors();
            if ( errors > 0 )
            {
                throw new SyntaxError();
            }

            CommonTree tree = (CommonTree) ret.getTree();
            // if ( tree != null )
            // System.out.println( "@@ " + tree.toStringTree() + " @@" );

            CommonTreeNodeStream treeStream = new CommonTreeNodeStream( tree );
            PxlTreeWalker walker = new PxlTreeWalker( treeStream );
            walker.file_input();
        }
        catch ( Exception e )
        {
            System.err.println( "Parser exception: " + e );
            e.printStackTrace();
        }

        return new PxlModule( name, moduleScope );
    }
}
